/*******************************************************************/
/*    Copyright (c) 1999-2020 by Spatial Corp.                     */
/*    All rights reserved.                                         */
/*    Protected by U.S. Patents 5,257,205; 5,351,196; 6,369,815;   */
/*                              5,982,378; 6,462,738; 6,941,251    */
/*    Protected by European Patents 0503642; 69220263.3            */
/*    Protected by Hong Kong Patent 1008101A                       */
/*******************************************************************/

//----------------------------- IopAcisMiscData.cpp -------------------------------

#include <iostream>

//ACIS
#include "lists.hxx"
#include "kernapi.hxx" 

//IOP
#include "SPAIopAcis.h"
#include "SPAIopPolicyOptions.h"
#include "SPAIopSampleUtilities.h"
#include "SPAIopPrintUtilities.h"
#include "SPAIopAcisSampleUtilities.h"
#include "SPAIopArgParser.h"
#include "SPAIopMiscUtils.h"
#include "SPAIopAcisEntityUtils.h"
#include "SPAIopSampleResultUtils.h"
#include "SPAIopPerformanceStatistics.h"
#include "SPAIopDocumentDataImporter.h"
#include "SPAIopDocumentDataImportResult.h"
#include "SPAIopOccurrenceUtils.h"
using namespace SPAIopSampleLogging;
using namespace SPAIopAcisProductStructureUtils;
using namespace SPAIopPrintUtilities;
using namespace SPAIopAcisPrintUtils;

//-----------------------------------------------------------------------------------------

typedef std::set< unsigned int > ProcessedPartIdList;

typedef std::set< unsigned int > ProcessedAsmRefIdList;

// License file
#include "spatial_license.h"
#include "spa_unlock_result.hxx"

int ImportMiscData(const wchar_t* ipSourceFilePath, const SPAIopPolicy& iPolicy);
bool GetEntityInfo(SPAIopEntity& iEntity, wchar_t* ioEntityInfo, int iLength);
int ProcessReference(const SPAIopInputPSReference& iInputPSRefrence, const SPAIopPolicy& iPolicy);
void ProcessInstance(const SPAIopInputPSInstance& iInputPSInstance);
void ProcessRootMiscData(const SPAIopInputProductStructure& iInputPS);
void ProcessAsmReference(const SPAIopInputPSReference& iInputPSRefrence);
int ProcessPartReference(const SPAIopInputPSReference& iInputPSRefrence, const SPAIopPolicy& iPolicy);
int ProcessPartMiscData(const SPAIopInputPSReference& iInputPSRefrence, const SPAIopPolicy& iPolicy);
bool ImportProductStructure(SPAIopDocument& ipSourceDoc, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS);

static ProcessedPartIdList alreadyProcessedPartIdList;
static ProcessedAsmRefIdList alreadyProcessedAsmRefIdList;
static bool _g_ProcessDocData = false;
InstancePathsPIDOccurrenceMap instPathPIDOccurrenceMap;

//-----------------------------------------------------------------------------------------

void PrintHelp()
{
	PrintMessage(L"Usage: IopAcisMiscData -i <full-input-file-path> [-o <full-output-dump-file-path>] [-p <full-policy-file-path>] [-log <full-log-file-path>] [-h] \n");
	PrintMessage(L"\t-i  : The absolute path to the input file. \n");
	PrintMessage(L"\t-o  : The absolute path to the output dump file(.txt). \n");
	PrintMessage(L"\t-p  : The absolute path to the policy file used to control optional behavior during operations. \n");
	PrintMessage(L"\t-docData: True or 1 to Get Document data. \n");
	PrintMessage(L"\t-log: The absolute path to the log file. \n");
	PrintMessage(L"\t-h  : This help.\n");
}

//-----------------------------------------------------------------------------------------

bool IsPartReferenceAlreadyProcessed(const SPAIopInputPSReference& iInputPSRefrence)
{
	bool partReferenceProcessed(true);

	unsigned int referenceID = iInputPSRefrence.GetUniqueID();
	ImportedPartIdList::iterator alreadyProcessedIter = alreadyProcessedPartIdList.find(referenceID);
	if (alreadyProcessedIter == alreadyProcessedPartIdList.end())
	{
		alreadyProcessedPartIdList.insert(referenceID);
		partReferenceProcessed = false;
	}

	return partReferenceProcessed;
}

//-----------------------------------------------------------------------------------------

bool IsAsmReferenceAlreadyProcessed(const SPAIopInputPSReference& iInputPSRefrence)
{
	bool asmReferenceProcessed(true);

	unsigned int referenceID = iInputPSRefrence.GetUniqueID();
	ImportedPartIdList::iterator alreadyProcessedIter = alreadyProcessedAsmRefIdList.find(referenceID);
	if (alreadyProcessedIter == alreadyProcessedAsmRefIdList.end())
	{
		alreadyProcessedAsmRefIdList.insert(referenceID);
		asmReferenceProcessed = false;
	}

	return asmReferenceProcessed;
}

//-----------------------------------------------------------------------------------------

int ProcessInputProductStructure(const SPAIopInputProductStructure& iInputPS, const SPAIopPolicy& iPolicy)
{
	alreadyProcessedPartIdList.clear();
	alreadyProcessedAsmRefIdList.clear();
	int result = SPAIopSampleResult_Import_Ok;
	int numInstances = iInputPS.GetRootInstancesCount();
	int i = 0;
	for (i = 0; i < numInstances; i++)
	{
		// Get Input PS info (instance, reference, ID)
		SPAIopInputPSInstance inputRootInstance = iInputPS.GetRootInstance(i);
		ProcessInstance(inputRootInstance);

		SPAIopInputPSReference inputReference = inputRootInstance.GetReference();
		int res = ProcessReference(inputReference, iPolicy);
		if (0 != res)
			result = res;
	}

	alreadyProcessedPartIdList.clear();
	alreadyProcessedAsmRefIdList.clear();
	return result;
}

//-----------------------------------------------------------------------------------------

int ProcessReference(const SPAIopInputPSReference& iInputPSRefrence, const SPAIopPolicy& iPolicy)
{
	int result = SPAIopSampleResult_Import_Ok;
	if (iInputPSRefrence.IsPart())
	{
		if (false == IsPartReferenceAlreadyProcessed(iInputPSRefrence))
		{
			result = ProcessPartReference(iInputPSRefrence, iPolicy);
		}
	}
	else
	{
		if (false == IsAsmReferenceAlreadyProcessed(iInputPSRefrence))
		{
			ProcessAsmReference(iInputPSRefrence);
		}

		int numInstances = iInputPSRefrence.GetInstancesCount();
		int i;
		for (i = 0; i < numInstances; i++)
		{
			SPAIopInputPSInstance currentInstance = iInputPSRefrence.GetInstance(i);
			ProcessInstance(currentInstance);

			SPAIopInputPSReference currentReference = currentInstance.GetReference();
			int res = ProcessReference(currentReference, iPolicy);
			if (0 != res)
				result = res;
		}
	}
	return result;
}

//-----------------------------------------------------------------------------------------

int ImportMiscData(const wchar_t* ipSourceFilePath, const SPAIopPolicy& iPolicy)
{
	int returnValue = SPAIopSampleResult_Import_Ok;

	// register modeller-specific functions with utility code
	SPAIopMiscUtils::GetEntityInfoCB = GetEntityInfo;

	// Create Source Document from input file	 
	SPAIopWString wSourceFilePath(ipSourceFilePath);
	SPAIopDocument sourceDoc(wSourceFilePath);

	SPAIopDocumentDataImporter psDataImporter;
	SPAIopDocumentDataImportResult rootPSDataResult;
	if (true == _g_ProcessDocData)
	{
		rootPSDataResult = psDataImporter.ImportWithPolicy(iPolicy, sourceDoc);
		ImportDocumentData(instPathPIDOccurrenceMap, iPolicy, rootPSDataResult);
		return returnValue;
	}

	// Import Input Product Structure 
	SPAIopInputProductStructure inputPS;

	bool gotPS = ImportProductStructure(sourceDoc, iPolicy, inputPS);
	if (true == gotPS)
	{
		ProcessRootMiscData(inputPS);

		returnValue = ProcessInputProductStructure(inputPS, iPolicy);
	}
	else
	{
		returnValue = SPAIopSampleResult_Import_PS_Fail;
	}

	return returnValue;
}

//-----------------------------------------------------------------------------------------
bool GetEntityInfo(SPAIopEntity& iEntity, wchar_t* ioEntityInfo, int iLength)
{
#ifdef _WINDOWS_SOURCE
	swprintf_s(ioEntityInfo, iLength, L"-not-found-");
#else
	swprintf(ioEntityInfo, (size_t)iLength, L"-not-found-");
#endif

	bool gotEntityInfo = false;

	if (SPAIopEntityType_ACIS == iEntity.GetEntityType())
	{
        //PID
		SPAIopAcisEntity& acisEntity = (SPAIopAcisEntity&)iEntity;
		SPAIopWString PIDW = acisEntity.GetPID();
		const wchar_t* pPIDWC = NULL;
		PIDW.ToWChar(pPIDWC);
		wcsncpy(ioEntityInfo, pPIDWC, iLength);
		ioEntityInfo[iLength - 1] = '\0';
		gotEntityInfo = (ioEntityInfo[0] != '\0');
	}
	else
	{
		std::wstring unhandledEntityMsg(L"Unhandled Entity Type");
#ifdef _WINDOWS_SOURCE
		swprintf_s(ioEntityInfo, iLength, L"%s", unhandledEntityMsg.c_str());
#else
		swprintf(ioEntityInfo, (size_t)iLength, L"%s", unhandledEntityMsg.c_str());
#endif
	}
	return gotEntityInfo;
}

//-----------------------------------------------------------------------------------------

void ProcessInstance(const SPAIopInputPSInstance& iInputPSInstance)
{
	PrintPSInstanceInfo(iInputPSInstance);

	SPAIopPSInstanceMiscData instanceMiscData = iInputPSInstance.GetMiscData();
	SPAIopMiscUtils::AccessPSInstanceMiscData(instanceMiscData);
}

//-----------------------------------------------------------------------------------------

void ProcessRootMiscData(const SPAIopInputProductStructure& iInputPS)
{
	PrintPSRootInfo(iInputPS);

	PrintMessage(L"\n Processing Root Misc Data. \n");

	SPAIopPSReferenceMiscData assemblyRootMiscData = iInputPS.GetMiscData();
	SPAIopMiscUtils::AccessPSReferenceMiscData(assemblyRootMiscData);
}

//-----------------------------------------------------------------------------------------

void ProcessAsmReference(const SPAIopInputPSReference& iInputPSRefrence)
{
	PrintPSReferenceInfo(iInputPSRefrence);

	SPAIopPSReferenceMiscData referenceMiscData = iInputPSRefrence.GetMiscData();
	SPAIopMiscUtils::AccessPSReferenceMiscData(referenceMiscData);
}

//-----------------------------------------------------------------------------------------

int ProcessPartReference(const SPAIopInputPSReference& iInputPSRefrence, const SPAIopPolicy& iPolicy)
{
	int result = SPAIopSampleResult_Import_Ok;
	PrintPSReferenceInfo(iInputPSRefrence);

	result = ProcessPartMiscData(iInputPSRefrence, iPolicy);


	if (SPAIopSampleResult_Import_Part_Fail == result)
	{
		unsigned int uniqueID = iInputPSRefrence.GetUniqueID();
		SPAIopWString storageNamePath = iInputPSRefrence.GetStorageName();
		SPAIopWString referenceName = iInputPSRefrence.GetReferenceName();

		SPAIopWString storageName;
		GetNameWithExtension(storageNamePath, storageName);

		const wchar_t* pStorageName = NULL;
		storageName.ToWChar(pStorageName);

		const wchar_t* pReferenceName = NULL;
		referenceName.ToWChar(pReferenceName);

		PrintMessage(L"\n FAILURE : Part failed to import. UniqueID = %d, Storage Name = %ls, Reference Name = %ls \n", uniqueID, pStorageName, pReferenceName);

	}

	return result;
}

//-----------------------------------------------------------------------------------------

int ProcessPartMiscData(const SPAIopInputPSReference& iInputPSRefrence, const SPAIopPolicy& iPolicy)
{
	// Import the Miscellaneous data of a part using the Import method of a 
	// SPAIopAcisPartImporter object.  Use try/catch exception handling
	// blocks to manage fatal errors.
	int result = SPAIopSampleResult_Import_Ok;
	ENTITY_LIST acisEntities;
	SPAIopAcisPartImporter partImporter;
	try
	{
		SPAIopPartDocument partReferneceDoc = iInputPSRefrence.GetDocument();
		SPAIopPartImportResult partImportResult = partImporter.ImportWithPolicy(iPolicy, partReferneceDoc, acisEntities);

		SPAIopMiscData miscData = partImportResult.GetMiscData();
		SPAIopMiscUtils::AccessMiscData(miscData);

		SPAIopEntityData entityData = partImportResult.GetEntityData();
		SPAIopAcisEntityUtils::AccessEntityData(entityData, acisEntities);
	}
	catch (SPAIopAcisError& /*error*/)
	{
		// Do not display the error because a missing part is not a fatal error
		//DisplayError( error );
		result = SPAIopSampleResult_Import_Part_Fail;
	}
	catch (...)
	{
		result = SPAIopSampleResult_Import_Part_Fail;
	}

	// When **completely** done with these entities, release them.
	// This is especially useful when sequentially importing and processing many parts in one session.
	if (0 < acisEntities.count())
	{
		SPAIopAcisSystem::ReleasePartEntities(acisEntities);
	}
	return result;
}

//-----------------------------------------------------------------------------------------

bool ImportProductStructure(SPAIopDocument& ipSourceDoc, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS)
{
	bool returnValue = true;

	try
	{
	    // Import the Assembly tree as a Product Structure.  Note that 
	    // importing a Product Structure does not import any PMI data associated
	    // with the model, which is done separately from part-associated reference nodes
	    // in the Product Structure.  Use try/catch exception handling blocks to 
	    // manage fatal errors.
	    

		// Create a Product Strcuture Importer
		SPAIopProductStructureImporter psImporter;
		SPAIopPSImportResult res = psImporter.ImportWithPolicy(iPolicy, ipSourceDoc, oInputPS);

		// Dump missing files in the Product Structure
		DumpMissingFilesInProductStructure(res);
	}
	catch (SPAIopAcisError& error)
	{
		SPAIopAcisPrintUtils::DisplayError(error);
		returnValue = false;
	}
	catch (...)
	{
		std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
		returnValue = false;
	}

	return returnValue;
}

//-----------------------------------------------------------------------------------------

int test_IopAcisMiscData(int argc, char* argv[])
{
	int result(1);
	SPAIopPerformanceStatistics printPerfoStatsObj;

	// Work-flow similar to Acis based app where Acis will be generally initialized before IOP is called.
	api_start_modeller(0);

	try
	{
		// IOP is called in its own scope.
		// This sample uses a guard class that initializes InterOp upon 
		// construction and terminates InterOp upon destruction.  All 
		// translations are done within the boundaries of a single 
		// initialization and shutdown.  InterOp may only be initialized 
		// and shutdown once per process.  Please do not use the guard 
		// classes multiple times per process.
		SPAIopAcisSystemInitGuard initGuard(SPATIAL_LICENSE);

		result = initGuard.GetInitStatus();

		if (SPA_UNLOCK_NO_MSG == result)
		{
			// Spatial products have been unlocked
			std::wcout << L"SUCCESS: SPAIopAcisSystem::Initialize. " << std::endl;
		}
		else if (SPA_UNLOCK_KEY_WARN_EXPIRE == result)
			std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_KEY_WARN_EXPIRE; your license key will expire soon!\n";
		else if (SPA_UNLOCK_REGISTRATION_WARN_EXPIRE == result)
			std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_REGISTRATION_WARN_EXPIRE; your registration will expire soon.\n";
		else
		{
			std::wcout << L"SPAIopAcisSystem::Initialize returned an unexpected value; Contact Spatial Customer Support or your Account Manager.\n";
			return 1;
		}
		result = 1;


		char* argFields[] = { "i", "o", "p", "docData", "h", "log" };
		int size = sizeof(argFields) / sizeof(*argFields);
		SPAIopArgParser parser(size, argFields, argc, argv, &PrintHelp);

		if (true == parser.IsSuccess())
		{
			const wchar_t* pSourceFilePath = NULL;
			bool hasInputFileName = parser.GetWStringValueFor("i", pSourceFilePath);

			const wchar_t* pOutputFilePath = NULL;
			bool hasOutputFileName = parser.GetWStringValueFor("o", pOutputFilePath);

			const wchar_t* pPolicyFilePath = NULL;
			bool hasPolicyFileName = parser.GetWStringValueFor("p", pPolicyFilePath);

			int dataFlag = 0;
			bool hasDataFlag = parser.GetIntegerValueFor("docData", dataFlag);

			if (hasDataFlag && (dataFlag != 0))
			{
				_g_ProcessDocData = true;
			}

			const wchar_t* pLogFilePath = NULL;
			bool hasLogFileName = parser.GetWStringValueFor("log", pLogFilePath);

			if (hasInputFileName)
			{
				if (true == hasOutputFileName)
				{
					if (false == OpenFileForConsoleOutput(pOutputFilePath))
						return 11;
				}

				SPAIopPolicy policy;
				if (NULL != pPolicyFilePath)
				{
					SPAIopWString policyFilePath(pPolicyFilePath);
					SPAIopPolicyFileStreamXML xmlPolicyStream;
					xmlPolicyStream.SetUnstreamFilePath(policyFilePath);

					policy.Deserialize(xmlPolicyStream);
				}

				SetOrUpdateLogFileInPolicy(pLogFilePath, policy);

				result = ImportMiscData(pSourceFilePath, policy);

				CloseFileForConsoleOutput();
			}
		}

	}
	catch (SPAIopAcisError& fatalErr)
	{
		DisplayError(fatalErr);
		result = 1;
	}
	catch (...)
	{
		PrintMessage(L"FAILURE: Unknown exception caught! ");
		result = 1;
	}

	api_stop_modeller();

	return result;
}

//-----------------------------------------------------------------------------------------

